package com.vcc.web.controller;


import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaCheckSafe;
import cn.dev33.satoken.stp.StpUtil;
import com.mybatisflex.core.paginate.Page;
import com.vcc.core.annotations.KycReject;
import com.vcc.core.annotations.RestPack;
import com.vcc.core.exception.NotFoundException;
import com.vcc.core.exception.ProgramException;
import com.vcc.core.model.file.rechargeLog.RechargeLogFilePojo;
import com.vcc.core.model.file.withdraw.WithdrawFilePojo;
import com.vcc.core.util.DateUtil;
import com.vcc.core.util.FileUtil;
import com.vcc.core.util.IdWorker;
import com.vcc.core.util.RegexUtil;
import com.vcc.data.entity.*;
import com.vcc.service.client.dto.withdraw.SubmitWithdrawDto;
import com.vcc.service.client.vo.withdraw.WithdrawVo;
import com.vcc.service.service.*;
import com.vcc.web.core.util.UserUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@RestController
@RequestMapping("withdraw")
public class WithdrawController {

    private static final Logger logger = LoggerFactory.getLogger(WithdrawController.class);

    @Resource
    private WithdrawService withdrawService;

    @Resource
    private ClientParamService clientParamService;

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private WalletService walletService;

    @Resource
    private WalletDetailService walletDetailService;

    @Resource
    private UserRateService userRateService;

    @Resource
    private UserService userService;

    @Resource
    private CurrencyService currencyService;


    @RestPack
    @KycReject
    @SaCheckLogin
    @PostMapping("createOrder")
    @SaCheckPermission("wallet:withdraw:create")
    @SaCheckSafe("WITHDRAW_CREATE")
    public Boolean createOrder(
            @RequestBody @Validated SubmitWithdrawDto submitWithdrawDto
    ) {
        Long id = UserUtil.getId();
        RLock lock = redissonClient.getLock("submit:withdraw:" + id);
        try {
            boolean tryLock = lock.tryLock();
            if (!tryLock) {
                throw new ProgramException("您当前有正在处理的申请,请稍后在提交");
            }
            StpUtil.closeSafe("WITHDRAW_CREATE");
            //单日最多提现多少次
            Long dayWithdrawMaxCount = Long.parseLong(clientParamService.getValueByCodeAndUserIdOrDefaultValue("DAY_WITHDRAW_MAX_COUNT", id, "3"));
            //单日最多提现多少钱
            BigDecimal dayWithdrawSumAmount = new BigDecimal(clientParamService.getValueByCodeAndUserIdOrDefaultValue("DAY_WITHDRAW_SUM_AMOUNT", id, "3000"));
            Long daySubmitCount = withdrawService.getDaySubmitCount(id);
            if (daySubmitCount >= dayWithdrawMaxCount) {
                throw new ProgramException("您当前提现申请以达到上限,请次日再次提交");
            }
            BigDecimal daySumSubmitAmount = withdrawService.getDaySumSubmitAmount(id);
            if (daySumSubmitAmount.add(submitWithdrawDto.getAmount()).compareTo(dayWithdrawSumAmount) >= 0) {
                throw new ProgramException("您当前提交申请金额已经达到上限,请次日在提交");
            }
            Wallet wallet = walletService.getByIdOpt(submitWithdrawDto.getWalletId()).orElseThrow(() -> new NotFoundException("没有当前账户"));
            if (!wallet.getUserId().equals(id)) {
                throw new ProgramException("非法操作");
            }
            BigDecimal withdrawRate = userRateService.getWithdrawRateById(id);

            BigDecimal walletBalance = walletService.subtractUsableQuotaById(wallet.getId(), submitWithdrawDto.getAmount(), 0, false);
            String orderId = IdWorker.getStringOrderId();
            WalletDetail walletDetail = walletDetailService.accountWithdraw(
                    "W" + orderId,
                    StringUtils.isBlank(submitWithdrawDto.getRemark()) ? "" : submitWithdrawDto.getRemark(),
                    submitWithdrawDto.getAmount(),
                    walletBalance,
                    wallet.getCurrencyId(),
                    wallet.getUserId()
            );
            Withdraw withdraw = Withdraw.builder()
                    .userId(id)
                    .address(submitWithdrawDto.getAddress())
                    .orderId(orderId)
                    .remark(StringUtils.isBlank(submitWithdrawDto.getRemark()) ? "" : submitWithdrawDto.getRemark())
                    .type(submitWithdrawDto.getType())
                    .status(2)
                    .currencyId(wallet.getCurrencyId())
                    .currencyCode(wallet.getCurrencyCode())
                    .amount(submitWithdrawDto.getAmount())
                    .withdrawCurrency(submitWithdrawDto.getCurrency())
                    .rate(withdrawRate)
                    .requestDate(new Date())
                    .free(submitWithdrawDto.getAmount().multiply(withdrawRate).setScale(2, RoundingMode.HALF_UP))
                    .build();
            withdrawService.save(withdraw);
        } catch (Exception e) {
            logger.error("创建订单失败，", e);
            if (e instanceof ProgramException) {
                throw e;
            }
            throw new ProgramException("提交失败,请稍后再试");
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return true;
    }


    @RestPack
    @GetMapping("paging")
    @SaCheckPermission("wallet:withdraw:view")
    public Page<WithdrawVo> paging(
            Integer pageNumber,
            Integer pageSize,
            Long targetUserId,
            String type,
            Integer status,
            String startDate,
            String endDate,
            String remark
    ) {
        User user = UserUtil.get();
        Long parentId = user.getParentId();
        Role role = UserUtil.getRole();
        Page<Withdraw> paging = withdrawService.paging(
                Page.of(
                        pageNumber == null ? 1 : pageNumber,
                        pageSize == null ? 20 : pageSize
                ),
                role,
                role.getHasAllData() && parentId != null && parentId != 0 ? parentId : user.getId(),
                targetUserId,
                type,
                startDate,
                endDate,
                remark,
                status);
        List<Withdraw> records = paging.getRecords();
        if (records == null || records.isEmpty()) {
            return Page.of(
                    paging.getPageNumber(),
                    paging.getPageSize()
            );
        }
        List<Long> userIds = records.stream().map(Withdraw::getUserId).distinct().toList();
        List<User> users = userService.listByIds(userIds);
        return paging.map(it -> {
            User accountUser = users.stream()
                    .filter(user1 -> user1.getId().equals(it.getUserId()))
                    .findFirst()
                    .orElse(null);
            String account = accountUser != null ? accountUser.getAccount() : "";
            return new WithdrawVo(it, account);
        });
    }

    @GetMapping("derive")
    @SaCheckPermission(value = "wallet:withdraw:derive")
    public void derive(
            Long targetUserId,
            String type,
            Integer status,
            String startDate,
            String endDate,
            String remark,
            HttpServletResponse response
    ) {
        User user = UserUtil.get();
        Long parentId = user.getParentId();
        Role role = UserUtil.getRole();
        if (RegexUtil.checkStringIsNull(startDate)) {
            throw new ProgramException("请选择开始时间");
        }
        if (RegexUtil.checkStringIsNull(endDate)) {
            throw new ProgramException("请选择结束时间");
        }
        Long pages = 1L;
        Page<Withdraw> paging;
        SXSSFWorkbook sxssfWorkbook = FileUtil.createWithdrawTitle(10000);
        List<Currency> currencyList = currencyService.list();
        do {
            paging = withdrawService.paging(
                    Page.of(
                            pages++,
                            1000
                    ),
                    role,
                    role.getHasAllData() && parentId != null && parentId != 0 ? parentId : user.getId(),
                    targetUserId,
                    type,
                    startDate,
                    endDate,
                    remark,
                    status);
            List<Withdraw> records = paging.getRecords();
            List<Long> userIds = records.stream().map(Withdraw::getUserId).distinct().toList();
            List<User> users = userService.listByIds(userIds);
            List<WithdrawFilePojo> list = paging.getRecords().stream().map(it -> {
                User accountUser = users.stream()
                        .filter(user1 -> user1.getId().equals(it.getUserId()))
                        .findFirst()
                        .orElse(null);
                String account = accountUser != null ? accountUser.getAccount() : "";
                Currency currency = currencyList.stream().filter(
                        c -> c.getId().equals(it.getCurrencyId())
                ).findFirst().orElse(null);
                return new WithdrawFilePojo(
                        it.getOrderId(),
                        DateUtil.format(it.getRequestDate()),
                        account,
                        it.getAmount(),
                        it.getAddress(),
                        it.getFree(),
                        it.getWithdrawCurrency(),
                        it.getOrderHash(),
                        it.getType(),
                        it.getRemark(),
                        it.getStatus(),
                        it.getAuditDate() != null ? DateUtil.format(it.getAuditDate()) : "",
                        "",
                        currency != null ? currency.getName() : ""
                );
            }).collect(Collectors.toList());
            FileUtil.deriveWithdrawData(sxssfWorkbook, list, paging.getPageNumber(), paging.getPageSize());
        } while (!(pages).equals(paging.getTotalPage() + 1));
        String fileName = System.currentTimeMillis() + "入金明细.xlsx";
        FileUtil.downXlsxFile(response, sxssfWorkbook, fileName);
    }
}
